package com.example.demo1

interface Monoid<A>{
    fun zero():A
    fun A.append(b:A):A
}

object stringConcatMonoid:Monoid<String>{
    override fun zero()=""

    override fun String.append(b: String)=this+b

}



fun <A> List<A>.sum(ma:Monoid<A>):A{
    val fa = this

    return ListFoldable.run {
        fa.fold(ma.zero())({
            s,i->
            ma.run {
                s.append(i)
            }
        })
    }

}


interface Kind2<out F,out A>
interface Functor2<F>{
    fun <A,B> Kind2<F,A>.map(f:(A)->B):Kind2<F,B>
}

class Option<F>{
    object K
}

class Effect{
    object K
}

class Parser{
    object K
}

object OptionFunctor:Functor2<Option.K>{
    override fun <A, B> Kind2<Option.K, A>.map(f: (A) -> B): Kind2<Option.K, B> {
        TODO()
    }

}

object EffectFunctor:Functor2<Effect.K>{
    override fun <A, B> Kind2<Effect.K, A>.map(f: (A) -> B): Kind2<Effect.K, B> {
        TODO()
    }

}

object ParserFunctor:Functor2<Parser.K>{
    override fun <A, B> Kind2<Parser.K, A>.map(f: (A) -> B): Kind2<Parser.K, B> {
        TODO()
    }

}


//fun <A,B,C,F> map2(fa:Kind2<F,A>,fb:Kind2<F,B>,f:(A,B)->C):Kind2<F,C>{
//    fa.flatMap{
//        a=>fb.map(b=>f(a,b))

//    }
//}


interface Monad<F>{
    fun <A> pure(a:A):Kind<F,A>
    fun <A,B> Kind<F,A>.flatMap(f:(A)->Kind<F,B>):Kind<F,B>
}

object ListMonad:Monad<List.K>{
    override fun <A> pure(a: A): Kind<List.K, A> {
        return Cons(a,Nil)
    }

    override fun <A, B> Kind<List.K, A>.flatMap(f: (A) -> Kind<List.K, B>): Kind<List.K, B> {

        val fa = this

        val empty:Kind<List.K,B> = Nil

        return ListFoldable.run {
            fa.fold(empty)({
                r,l->
                append(r,f(l))
            })
        }


    }


    private fun <A> append(fa:Kind<List.K,A>,fb:Kind<List.K,A>):Kind<List.K,A>{
        return if(fa is Cons){
            Cons(fa.head, append(fa.tail,fb).unwrap())
        }else{
            fb
        }
    }

}












